/*****************************************************************************/
/* File: Ball.cpp	                                                         */
/* Author: rszulgo                                                           */
/*                                                                           */
/* Description:																 */
/*	Representation of ball object											 */
/*****************************************************************************/
#include "Ball.h"

CBall::CBall(CPhysics* pPhysics)
{
	//non-ODE init routines
	m_bMarked = false;

	//ODE init routines:

	m_body = dBodyCreate( pPhysics->GetWorld() );
	
	m_geom = dCreateSphere(pPhysics->GetSpace(), 0.47);
		
	dGeomSetBody(m_geom, m_body);

	dMassSetSphere(&m_mass, 1, 0.47f);
	dBodySetMass(m_body, &m_mass);

	dBodySetPosition(m_body, 0.0f, 0.47f, 8.0f);

	dBodySetData(m_body, this);

	//set initial velocity and speed
	m_fSpeed = 13.0f;

	double fVel[3] = {1.5f, 0.0f, 1.0f};	//teporary vector with direction of velocity before normalization
	CUtils::Vec3Norm(fVel);	//normalize direction vector
	CUtils::Vec3Mult(fVel, m_fSpeed);

	dBodySetLinearVel(m_body, fVel[0], fVel[1], fVel[2]);
	
	// set ball surface
	GLfloat fWhiteSurface[4] = {1.0f, 1.0f, 1.0f, 1.0f};

	for (int i=0; i<4; i++)
	{
		m_fBallSurface[i] = fWhiteSurface[i];
	}
//	dRSetIdentity (R);
//	dBodySetRotation(body, R);
}

CBall::~CBall(void)
{
	dBodyDestroy(m_body);
	dGeomDestroy(m_geom);
}

int CBall::Draw()
{
	glPushMatrix();

	SetupMatrices();
	
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, m_fBallSurface);
	gluSphere(gluNewQuadric(), 0.47, 60, 60);

	glPopMatrix();
	return 0;
}

int CBall::Update()
{
	//check if ball is trapped by bat
	if(m_bTrapped)
	{
		//if so, move with bat
		const double* dBatPos = m_pBatRecent->GetPosition();

		//for 1-st player ball should have higher z-value, for 2-nd
		//should have lower z-value. Because of this we need sign of z-value of Bat
		float fSign = (float)dBatPos[2]/abs((float)dBatPos[2]);

		dBodySetPosition(m_body, dBatPos[0], 0.47, dBatPos[2] - (fSign*1.0));

		//check if player released the ball
		if(m_pBatRecent->GetRelease() == true)
		{
			//if so, release ball and set dir vector to up-right (like in Arkanoid :P )
			//up-right in player coordinates - so once again we'll need fSign

			m_bTrapped = false;	//release ball
			dBodySetLinearVel(m_body, 1.0*fSign, 0.0, -1.0*fSign);	//set velocity
		}

		//get bat surface to mark the ball
		GLfloat* fSurface = m_pBatRecent->GetSurface();
		SetSurface(fSurface);
	}
	else
	{
		//if ball is not traped, move normally

		const double* dLinVel = dBodyGetLinearVel(m_body);
		//y should be 0, because ball should alvays move in a horizontal direction
		double dResultVel[3] = {dLinVel[0], 0.0, dLinVel[2]};

		CUtils::Vec3Norm(dResultVel);
		CUtils::Vec3Mult(dResultVel, m_fSpeed);

		dBodySetLinearVel(m_body, dResultVel[0], dResultVel[1], dResultVel[2]);
	}

	m_pBatRecent->SetRelease(false);	//always reset release
	return 0;
}

int CBall::BatBounce(CBat* pBat)
{

	//After hitting a bat ball is bounced out in a direction that is determined by the
	//hitted part of the bat. If hit takes place in the center of the Bat, ball is
	//throw straight forward (in direction of Bat's normal). If hit was in the corner
	//of Bat, ball is throw in the angle of 45 degrees in left or right.

	double fBounceDir[3] = {0.0f, 0.0f, 0.0f};

	//if ball is higher than a bat, probably it's the lower bat
	const double* dBallPos = dBodyGetPosition(m_body);
	const double* dBatPos = pBat->GetPosition();

	double dBatLength = pBat->GetLength();

	if(dBallPos[2] <= dBatPos[2])
	{
		//ok, it's lower bat, so rebound the ball up
		fBounceDir[2] = -1.0;
	}
	else
	{
		fBounceDir[2] = 1.0;
	}

	//now, compute left-right parameter. It depends on the location of the ball
	//against the bat on the x axis.
	fBounceDir[0] = (double)(2*(dBallPos[0] - dBatPos[0]))/dBatLength;

	//normalize velcity direction, and multiply it by speed
	CUtils::Vec3Norm(fBounceDir);
	CUtils::Vec3Mult(fBounceDir, m_fSpeed);

	dBodySetLinearVel(m_body, fBounceDir[0], fBounceDir[1], fBounceDir[2]);

	//set this bat as recently touched ball
	m_pBatRecent = pBat;

	//get bat surface to mark the ball
	GLfloat* fSurface = m_pBatRecent->GetSurface();
	SetSurface(fSurface);

	return 0;
}

int CBall::Out(CBat* pBat)
{
	m_pBatRecent = pBat;
	m_bTrapped = true;

	return 0;
}